package org.optionparser;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;

import junit.framework.TestCase;

import org.junit.Test;
import org.optionparser.OptionParser;
import org.optionparser.ParseException;

public class OptionParserTest extends TestCase {

    // -------------------------------------------
    // Constants
    // -------------------------------------------
    private static String[] DEFAULT_ARGUMENTS = { "-v", "--help", "--loglevel",
            "20", "--", "--value1", "value2" };

    // -------------------------------------------
    // Test help printing
    // -------------------------------------------

    /**
     * Print without defined options should not throw exception
     */
    @Test
    public void testPrintEmptyHelp() {
        ByteArrayOutputStream nullOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(nullOut));

        OptionParser parser = new OptionParser(DEFAULT_ARGUMENTS);
        parser.printOptionsInfo();
        System.setOut(System.out);
    }

    /**
     * Print should not throw exception
     */
    @Test
    public void testPrintWithDefinedOptions() {
        ByteArrayOutputStream nullOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(nullOut));

        OptionParser parser = new OptionParser(DEFAULT_ARGUMENTS);
        parser.addOptionalBooleanOption("b", "bool", ParameterType.Optional,
                "Boolean option", "");
        parser.addOptionalIntegerOption("i", "integer", ParameterType.Optional,
                "Integer optino", 0, 0, "");
        parser.addOptionalNoParameterOption("n", "noparameter",
                "No parameter option");
        parser.addOptionalStringOption("s", "string", ParameterType.Optional,
                "String option", null, "");
        parser.printOptionsInfo();
        System.setOut(System.out);

    }

    // -------------------------------------------
    // Unparsed arguments
    // -------------------------------------------

    /**
     * Test function containsOption on unparsed arguments
     */
    @Test
    public void testUnparsedContainsOption() {

        OptionParser parser = new OptionParser(DEFAULT_ARGUMENTS);
        parser.addOptionalNoParameterOption("v", "version",
                "Print program version");
        try {
            parser.containsOption("v");
            fail("Expected ParserException at checking options withou parsing");
        } catch (ParseException expectedException) {

            assertEquals(
                    "Wrong type of exception at checking unparsed options",
                    ParseExceptionType.CMDNotParsedYet, expectedException.type);
        }

    }

    /**
     * Test function getResult on unparsed arguments
     */
    @Test
    public void testUnparsedGetResult() {

        OptionParser parser = new OptionParser(DEFAULT_ARGUMENTS);
        parser.addOptionalNoParameterOption("v", "version",
                "Print program version");
        try {
            parser.getResult("v");
            fail("Expected ParserException at getting options withou parsing");
        } catch (ParseException expectedException) {

            assertEquals("Wrong type of exception at getting unparsed options",
                    ParseExceptionType.CMDNotParsedYet, expectedException.type);
        }

    }

    private static String EXPECTED_EXCEPTION_ON_GETTING_UNPARSED_DATA =
            "Expected ParserException at getting all arguments withou parsing";
    private static String EXPECTED_OTHER_EXCEPTION_ON_GETTING_UNPARSED_DATA =
            "Wrong type of exception at getting all arguments without parsing";

    /**
     * Test function getResult on unparsed arguments
     */
    @Test
    public void testUnparsedGetArguments() {

        OptionParser parser = new OptionParser(DEFAULT_ARGUMENTS);
        parser.addOptionalNoParameterOption("v", "version",
                "Print program version");
        try {
            parser.getArguments();
            fail(EXPECTED_EXCEPTION_ON_GETTING_UNPARSED_DATA);
        } catch (ParseException expectedException) {

            assertEquals(EXPECTED_OTHER_EXCEPTION_ON_GETTING_UNPARSED_DATA,
                    ParseExceptionType.CMDNotParsedYet, expectedException.type);
        }

    }

    /**
     * Test function getResult on unparsed arguments
     */
    @Test
    public void testUnparsedGetResults() {

        OptionParser parser = new OptionParser(DEFAULT_ARGUMENTS);
        parser.addOptionalNoParameterOption("v", "version",
                "Print program version");
        try {
            parser.getResults();
            fail(EXPECTED_EXCEPTION_ON_GETTING_UNPARSED_DATA);
        } catch (ParseException expectedException) {

            assertEquals(EXPECTED_OTHER_EXCEPTION_ON_GETTING_UNPARSED_DATA,
                    ParseExceptionType.CMDNotParsedYet, expectedException.type);
        }

    }

    // -------------------------------------------
    // -------------------------------------------
    // Parsed arguments
    // -------------------------------------------
    // -------------------------------------------

    // -------------------------------------------
    // test empty input
    // -------------------------------------------

    private static int EMPTY_LIST_SIZE = 0;

    private static String EMPTY_STRING = "";

    private static String[] NO_ARGUMETNS = {};

    private static String[] EMPTY_ARGUMENTS = { EMPTY_STRING, EMPTY_STRING };

    /**
     * Test parsing of no input argument
     * 
     * @throws ParseException
     */
    @Test
    public void testNoArgumentParsing() throws ParseException {

        testUndefinedOptionalArguments(NO_ARGUMETNS);
        testUndefinedOptionalArguments(EMPTY_ARGUMENTS);

    }

    @Test
    public void testNullInputArguments() throws ParseException {
        try {
            testUndefinedOptionalArguments(null);
        } catch (NullPointerException nullException) {
            fail("Parser fails on null input (arguments)");
        }
    }

    private void testUndefinedOptionalArguments(String[] noArguments)
            throws ParseException {
        OptionParser parser = new OptionParser(noArguments);
        parser.addOptionalBooleanOption("b", "bool", ParameterType.Optional,
                "Boolean option", "");
        parser.addOptionalIntegerOption("i", "integer", ParameterType.Optional,
                "Integer option", 0, 0, "");
        parser.addOptionalNoParameterOption("n", "noparameter",
                "No parameter option");
        parser.addOptionalStringOption("s", "string", ParameterType.Optional,
                "String option", null, "");
        parser.parse();

        // test, that options are not parsed
        assertFalse("Option b should be undefined", parser.containsOption("b"));
        assertFalse("Option i should be undefined", parser.containsOption("i"));
        assertFalse("Option n should be undefined", parser.containsOption("n"));
        assertFalse("Option s should be undefined", parser.containsOption("s"));

        // test, that options are not parsed and has no value
        assertNull("Value of option b should be null", parser.getResult("b"));
        assertNull("Value of option i should be null", parser.getResult("i"));
        assertNull("Value of option n should be null", parser.getResult("n"));
        assertNull("Value of option s should be null", parser.getResult("s"));

        // test result collections
        int expectedLength = EMPTY_LIST_SIZE;
        if (TestingUtils.arrayIsNotNull(noArguments)) {
            expectedLength = noArguments.length;
        }

        assertEquals("Parsing no arguments shoud return empty list", parser
                .getArguments().size(), expectedLength);
        assertEquals("Parsing no options shoud return empty map", parser
                .getResults().size(), EMPTY_LIST_SIZE);

    }

    // -------------------------------------------
    // -------------------------------------------
    // test boolean option
    // -------------------------------------------
    // -------------------------------------------

    private static String ERROR_STRING_UNCORRECT_PARSED_ARGUMENT =
            "Value of option %s was not correctly parsed in argument input %s ";

    public static String prepareErrorMessageForOptionWithValue(
            String optionName, String[] inputArguments) {
        return String.format(ERROR_STRING_UNCORRECT_PARSED_ARGUMENT,
                optionName, Arrays.toString(inputArguments));

    }

    private static String ERROR_STRING_UNCORRECT_PARSE =
            "Option %s without defined value was not correctly "
                    + "parsed in argument input %s ";

    public static String prepareErrorMessageForOptionWithoutValue(
            String optionName, String[] inputArguments) {
        return String.format(ERROR_STRING_UNCORRECT_PARSE, optionName,
                Arrays.toString(inputArguments));

    }

    /**
     * Create parser with boolean option and some other options
     * (otherOptionsShortNames)
     * 
     * @param shortName
     *            option's short name
     * @param longName
     *            option's long name
     * @param arguments
     *            input arguments
     * @param otherOptionsShortNames
     *            function will create optional no parameter options
     * @return parser with defined boolean option
     */
    private static OptionParser createParserWithBoolOption(
            Boolean optionIsRequired, ParameterType valueType,
            String shortName, String longName, String[] arguments,
            String[] otherOptionsShortNames) {

        OptionParser resultParser = new OptionParser(arguments);
        if (optionIsRequired) {
            resultParser.addRequiredBooleanOption(shortName, longName,
                    valueType, "", "");
        } else {
            resultParser.addOptionalBooleanOption(shortName, longName,
                    valueType, "", "");
        }
        for (String optionsShortName : otherOptionsShortNames) {
            resultParser.addOptionalNoParameterOption(optionsShortName, null,
                    "");

        }
        return resultParser;

    }

    private static String BOOL_SHORT_NAME = "b";
    private static String BOOL_SHORT_NAME_WITH_PREFIX = "-" + BOOL_SHORT_NAME;
    private static String BOOL_LONG_NAME = "bool";
    private static String BOOL_LONG_NAME_WITH_PREFIX = "--" + BOOL_LONG_NAME;

    private static String[] OTHER_OPTIONS_WITH_PREFIX = { "-a", "-c" };
    private static String[] OTHER_OPTIONS_WITHOUT_PREFIX = { "a", "c" };

    private String[][] createInputCombinationsWithValue(String optionShortName,
            String optionLongName, String optionValue) {
        // array of values
        String[] currentOptionValue = { optionValue };

        // create combinations
        String[][] longNameCombinationsWithValue =
                TestingUtils.createAllCombinationsWithValue(optionLongName,
                        currentOptionValue, true, OTHER_OPTIONS_WITH_PREFIX);
        String[][] shortNameCombinationsWithValue =
                TestingUtils.createAllCombinationsWithValue(optionShortName,
                        currentOptionValue, false, OTHER_OPTIONS_WITH_PREFIX);
        return TestingUtils.concatenateStringArrays(
                longNameCombinationsWithValue, shortNameCombinationsWithValue);

    }

    private String[][] createInputCombinationsWithoutValue(
            String shortNameWithPrefix, String longNameWithPrefix) {

        String[][] longNameCombinationsWithoutValue =
                TestingUtils.createAllCombinationsWithoutValue(
                        longNameWithPrefix, OTHER_OPTIONS_WITH_PREFIX);
        String[][] shortNameCombinationsWithoutValue =
                TestingUtils.createAllCombinationsWithoutValue(
                        shortNameWithPrefix, OTHER_OPTIONS_WITH_PREFIX);

        return TestingUtils.concatenateStringArrays(
                longNameCombinationsWithoutValue,
                shortNameCombinationsWithoutValue);

    }

    private void testBooleanOptionWithoutValue(boolean optionIsRequired,
            ParameterType valueType, boolean exceptionIsExpected,
            ParseExceptionType expectedException) throws ParseException {

        String[][] noValueCombinations =
                createInputCombinationsWithoutValue(
                        BOOL_SHORT_NAME_WITH_PREFIX, BOOL_LONG_NAME_WITH_PREFIX);

        for (String[] inputArgumentsWithoutValue : noValueCombinations) {

            OptionParser parser =
                    createParserWithBoolOption(optionIsRequired, valueType,
                            BOOL_SHORT_NAME, BOOL_LONG_NAME,
                            inputArgumentsWithoutValue,
                            OTHER_OPTIONS_WITHOUT_PREFIX);

            try {

                parser.parse();
                if (exceptionIsExpected) {
                    fail("Boolean option with " + valueType
                            + " value does not throw exception for input"
                            + Arrays.toString(inputArgumentsWithoutValue));
                }
            } catch (ParseException exception) {
                if (exceptionIsExpected) {
                    assertEquals("Wrong type of exception at parsing "
                            + valueType + " value", expectedException,
                            exception.type);

                } else {
                    throw exception;
                }
            }

            if (exceptionIsExpected == false) {
                String errorMessageForShortOption =
                        prepareErrorMessageForOptionWithoutValue(
                                BOOL_SHORT_NAME, inputArgumentsWithoutValue);

                assertEquals(errorMessageForShortOption, EMPTY_STRING,
                        parser.getResult(BOOL_SHORT_NAME));

                String errorMessageFoLongOption =
                        prepareErrorMessageForOptionWithoutValue(
                                BOOL_LONG_NAME, inputArgumentsWithoutValue);
                assertEquals(errorMessageFoLongOption, EMPTY_STRING,
                        parser.getResult(BOOL_LONG_NAME));

            }

        }

    }

    private void testBooleanOptionWithValue(boolean optionIsRequired,
            ParameterType valueType, String[] inputValues,
            boolean exceptionIsExpected, ParseExceptionType expectedException)
            throws ParseException {

        if (TestingUtils.arrayIsNull(inputValues)) {
            return;
        }
        for (String boolValue : inputValues) {

            String[][] valueCombinations =
                    createInputCombinationsWithValue(
                            BOOL_SHORT_NAME_WITH_PREFIX,
                            BOOL_LONG_NAME_WITH_PREFIX, boolValue);

            for (String[] inputArgumentsWithValue : valueCombinations) {

                OptionParser parser =
                        createParserWithBoolOption(optionIsRequired, valueType,
                                BOOL_SHORT_NAME, BOOL_LONG_NAME,
                                inputArgumentsWithValue,
                                OTHER_OPTIONS_WITHOUT_PREFIX);

                try {
                    parser.parse();
                    if (exceptionIsExpected) {
                        fail("Boolean option with " + valueType
                                + " value does not throw exception for input"
                                + Arrays.toString(inputArgumentsWithValue));
                    }
                } catch (ParseException exception) {
                    if (exceptionIsExpected) {
                        assertEquals("Wrong type of exception at parsing "
                                + valueType + " value", expectedException,
                                exception.type);

                    } else {
                        throw exception;
                    }
                }

                if (exceptionIsExpected == false) {

                    String errorMessageForShortOption =
                            prepareErrorMessageForOptionWithValue(
                                    BOOL_SHORT_NAME, inputArgumentsWithValue);
                    assertEquals(errorMessageForShortOption, boolValue,
                            parser.getResult(BOOL_SHORT_NAME));

                    String errorMessageFoLongOption =
                            prepareErrorMessageForOptionWithValue(
                                    BOOL_LONG_NAME, inputArgumentsWithValue);
                    assertEquals(errorMessageFoLongOption, boolValue,
                            parser.getResult(BOOL_LONG_NAME));

                }

            }

        }
    }

    // -------------------------------------------
    // test optional boolean option
    // -------------------------------------------

    @Test
    public void testOptionalBoolOption() throws ParseException {

        String[] correctInputValues = { "true", "false" };
        String[] incorrectInputValues = { "tru", "FALSE" };

        // parameter is optional
        testBooleanOptionWithValue(false, ParameterType.Optional,
                correctInputValues, false, null);
        testBooleanOptionWithValue(false, ParameterType.Optional,
                incorrectInputValues, true,
                ParseExceptionType.WrongArgumentClass);
        testBooleanOptionWithoutValue(false, ParameterType.Optional, false,
                null);

        // parameter is forbidden
        testBooleanOptionWithValue(false, ParameterType.Forbidden,
                correctInputValues, true, ParseExceptionType.ForbiddenArgument);
        testBooleanOptionWithValue(false, ParameterType.Forbidden,
                incorrectInputValues, true,
                ParseExceptionType.ForbiddenArgument);
        testBooleanOptionWithoutValue(false, ParameterType.Forbidden, false,
                null);

        // parameter is required
        testBooleanOptionWithValue(false, ParameterType.Required,
                correctInputValues, false, null);
        testBooleanOptionWithValue(false, ParameterType.Required,
                incorrectInputValues, true,
                ParseExceptionType.WrongArgumentClass);
        testBooleanOptionWithoutValue(false, ParameterType.Required, true,
                ParseExceptionType.RequiredOptionNotUsed);
    }

    // -------------------------------------------
    // test required boolean option
    // -------------------------------------------

    @Test
    public void testRequiredBoolOption() throws ParseException {
        String[] correctInputValues = { "true", "false" };
        String[] incorrectInputValues = { "tru", "FALSE" };

        // parameter is optional
        testBooleanOptionWithValue(true, ParameterType.Optional,
                correctInputValues, false, null);
        testBooleanOptionWithValue(true, ParameterType.Optional,
                incorrectInputValues, true,
                ParseExceptionType.WrongArgumentClass);
        testBooleanOptionWithoutValue(true, ParameterType.Optional, false, null);

        // parameter is forbidden
        testBooleanOptionWithValue(true, ParameterType.Forbidden,
                correctInputValues, true, ParseExceptionType.ForbiddenArgument);

        // 2 possible errors (wrong input + forbidden value): expected
        // ForbiddenArgument exception
        testBooleanOptionWithValue(true, ParameterType.Forbidden,
                incorrectInputValues, true,
                ParseExceptionType.ForbiddenArgument);
        testBooleanOptionWithoutValue(true, ParameterType.Forbidden, false,
                null);

        // parameter is required
        testBooleanOptionWithValue(true, ParameterType.Required,
                correctInputValues, false, null);
        testBooleanOptionWithValue(true, ParameterType.Required,
                incorrectInputValues, true,
                ParseExceptionType.WrongArgumentClass);
        testBooleanOptionWithoutValue(true, ParameterType.Required, true,
                ParseExceptionType.RequiredOptionNotUsed);
    }

    @Test
    public void testRequiredBoolOptionWithUndefinedInput()
            throws ParseException {

        testRequiredBoolWithEmptyInput(NO_ARGUMETNS);
        testRequiredBoolWithEmptyInput(OTHER_OPTIONS_WITHOUT_PREFIX);
    }

    private void testRequiredBoolWithEmptyInput(String[] inputArguments) {

        OptionParser parser =
                createParserWithBoolOption(true, ParameterType.Optional,
                        BOOL_SHORT_NAME, BOOL_LONG_NAME, inputArguments,
                        OTHER_OPTIONS_WITHOUT_PREFIX);

        try {

            parser.parse();

            fail("Required boolean option with " + ParameterType.Optional
                    + " value does not throw exception for input"
                    + Arrays.toString(inputArguments));

        } catch (ParseException exception) {

            assertEquals("Wrong type of exception at parsing forbiden value",
                    ParseExceptionType.RequiredOptionNotUsed, exception.type);

        }

    }

    // -------------------------------------------
    // -------------------------------------------
    // test integer option
    // -------------------------------------------
    // -------------------------------------------

    private static class IntegerTestingData {
        public Integer lowerBoundary = 0;
        public Integer upperBoundary = 0;
        public Integer value = 0;

        public IntegerTestingData(Integer lowerBoudary, Integer upperBoundary,
                Integer value) {
            this.lowerBoundary = lowerBoudary;
            this.upperBoundary = upperBoundary;
            this.value = value;
        }

        public String getStringValue() {
            return value.toString();
        }
    }

    private static IntegerTestingData CORRECT_ONE_INTEGER_INPUT =
            new IntegerTestingData(0, 0, 0);
    private static String INTEGER_SHORT_NAME = "i";
    private static String INTEGER_SHORT_NAME_WITH_PREFIX = "-"
            + INTEGER_SHORT_NAME;
    private static String INTEGER_LONG_NAME = "integer";
    private static String INTEGER_LONG_NAME_WITH_PREFIX = "--"
            + INTEGER_LONG_NAME;

    private static IntegerTestingData[] createCorrectIntegerData() {

        ArrayList<IntegerTestingData> result =
                new ArrayList<IntegerTestingData>();

        result.add(new IntegerTestingData(0, 0, 0));
        result.add(new IntegerTestingData(0, Integer.MAX_VALUE, 1));
        result.add(new IntegerTestingData(0, Integer.MAX_VALUE,
                Integer.MAX_VALUE));

        return result.toArray(new IntegerTestingData[0]);
    }

    private static IntegerTestingData[] createCorrectNegativeIntegerData() {

        ArrayList<IntegerTestingData> result =
                new ArrayList<IntegerTestingData>();

        result.add(new IntegerTestingData(Integer.MIN_VALUE, 0,
                Integer.MIN_VALUE));
        result.add(new IntegerTestingData(Integer.MIN_VALUE, 0, -1));

        return result.toArray(new IntegerTestingData[0]);
    }

    private static IntegerTestingData[] createIncorrectIntegerData() {

        ArrayList<IntegerTestingData> result =
                new ArrayList<IntegerTestingData>();

        result.add(new IntegerTestingData(0, 0, 1));

        result.add(new IntegerTestingData(-1, 1, Integer.MAX_VALUE));
        result.add(new IntegerTestingData(Integer.MIN_VALUE, 0, 1));
        result.add(new IntegerTestingData(1, Integer.MAX_VALUE, 0));

        return result.toArray(new IntegerTestingData[0]);
    }

    private static OptionParser createParserWithIntegerOption(
            Boolean optionIsRequired, ParameterType valueType,
            String shortName, String longName, IntegerTestingData boundaries,
            String[] arguments, String[] otherOptionsShortNames) {

        OptionParser resultParser = new OptionParser(arguments);
        if (optionIsRequired) {
            resultParser.addRequiredIntegerOption(shortName, longName,
                    valueType, "", boundaries.lowerBoundary,
                    boundaries.upperBoundary, "");
        } else {
            resultParser.addOptionalIntegerOption(shortName, longName,
                    valueType, "", boundaries.lowerBoundary,
                    boundaries.upperBoundary, "");
        }

        for (String optionsShortName : otherOptionsShortNames) {
            resultParser.addOptionalNoParameterOption(optionsShortName, null,
                    "");

        }
        return resultParser;

    }

    private void testIntegerOptionWithValue(boolean optionIsRequired,
            ParameterType valueType, IntegerTestingData[] inputData,
            boolean exceptionIsExpected, ParseExceptionType expectedException)
            throws ParseException {

        if (TestingUtils.arrayIsNull(inputData)) {
            return;
        }

        for (IntegerTestingData argumentValue : inputData) {

            String[][] valueCombinations =
                    createInputCombinationsWithValue(
                            INTEGER_SHORT_NAME_WITH_PREFIX,
                            INTEGER_LONG_NAME_WITH_PREFIX,
                            argumentValue.getStringValue());

            for (String[] inputArgumentsWithValue : valueCombinations) {

                OptionParser parser =
                        createParserWithIntegerOption(optionIsRequired,
                                valueType, INTEGER_SHORT_NAME,
                                INTEGER_LONG_NAME, argumentValue,
                                inputArgumentsWithValue,
                                OTHER_OPTIONS_WITHOUT_PREFIX);
                // TODO
                System.out.print(valueType
                        + Arrays.toString(inputArgumentsWithValue));
                try {
                    parser.parse();
                    if (exceptionIsExpected) {
                        fail("Integer option with " + valueType
                                + " value does not throw exception for input"
                                + Arrays.toString(inputArgumentsWithValue));
                    }
                } catch (ParseException exception) {
                    if (exceptionIsExpected) {
                        assertEquals("Wrong type of exception at parsing "
                                + valueType + " value", expectedException,
                                exception.type);
                        // TODO
                        System.out.print(" exception catched - OK");
                    } else {
                        // TODO
                        System.out.println(" - undefined exception ");
                        throw exception;
                    }
                }

                if (exceptionIsExpected == false) {

                    String errorMessageForShortOption =
                            prepareErrorMessageForOptionWithValue(
                                    INTEGER_SHORT_NAME, inputArgumentsWithValue);
                    assertEquals(errorMessageForShortOption,
                            argumentValue.getStringValue(),
                            parser.getResult(INTEGER_SHORT_NAME));

                    String errorMessageFoLongOption =
                            prepareErrorMessageForOptionWithValue(
                                    INTEGER_LONG_NAME, inputArgumentsWithValue);
                    assertEquals(errorMessageFoLongOption,
                            argumentValue.getStringValue(),
                            parser.getResult(INTEGER_LONG_NAME));
                    // TODO
                    System.out.print(" parsed - OK");

                }
                // TODO
                System.out.println("");
            }

        }
    }

    private void testIntegerOptionWithoutValue(boolean optionIsRequired,
            ParameterType valueType, boolean exceptionIsExpected,
            ParseExceptionType expectedException) throws ParseException {

        String[][] noValueCombinations =
                createInputCombinationsWithoutValue(
                        INTEGER_SHORT_NAME_WITH_PREFIX,
                        INTEGER_LONG_NAME_WITH_PREFIX);

        for (String[] inputArgumentsWithoutValue : noValueCombinations) {

            OptionParser parser =
                    createParserWithIntegerOption(optionIsRequired, valueType,
                            INTEGER_SHORT_NAME, INTEGER_LONG_NAME,
                            CORRECT_ONE_INTEGER_INPUT,
                            inputArgumentsWithoutValue,
                            OTHER_OPTIONS_WITHOUT_PREFIX);

            try {
                // TODO
                System.out.print(valueType
                        + Arrays.toString(inputArgumentsWithoutValue));
                parser.parse();
                if (exceptionIsExpected) {
                    fail("Integer option with " + valueType
                            + " value does not throw exception for input"
                            + Arrays.toString(inputArgumentsWithoutValue));
                }
            } catch (ParseException exception) {
                if (exceptionIsExpected) {
                    assertEquals("Wrong type of exception at parsing "
                            + valueType + " value", expectedException,
                            exception.type);
                    // TODO
                    System.out.print(" exception catched - OK");
                } else {
                    throw exception;
                }
            }

            if (exceptionIsExpected == false) {
                String errorMessageForShortOption =
                        prepareErrorMessageForOptionWithoutValue(
                                INTEGER_SHORT_NAME, inputArgumentsWithoutValue);

                assertEquals(errorMessageForShortOption, EMPTY_STRING,
                        parser.getResult(INTEGER_SHORT_NAME));

                String errorMessageFoLongOption =
                        prepareErrorMessageForOptionWithoutValue(
                                INTEGER_LONG_NAME, inputArgumentsWithoutValue);
                assertEquals(errorMessageFoLongOption, EMPTY_STRING,
                        parser.getResult(INTEGER_LONG_NAME));
                // TODO
                System.out.print(" parsed - OK");
            }
            // TODO
            System.out.println("");
        }

    }

    // -------------------------------------------
    // test optional boolean option
    // -------------------------------------------

    @Test
    public void testOptionalIntegerOption() throws ParseException {

        IntegerTestingData[] correctInput = createCorrectIntegerData();
        IntegerTestingData[] incorrectInput = createIncorrectIntegerData();

        // opntional value
        testIntegerOptionWithValue(false, ParameterType.Optional, correctInput,
                false, null);
        testIntegerOptionWithValue(false, ParameterType.Optional,
                incorrectInput, true, ParseExceptionType.ArgumentOutOfBounds);
        testIntegerOptionWithoutValue(false, ParameterType.Optional, false,
                null);

        // forbidden value
        testIntegerOptionWithValue(false, ParameterType.Forbidden,
                correctInput, true, ParseExceptionType.ForbiddenArgument);
        testIntegerOptionWithValue(false, ParameterType.Forbidden,
                incorrectInput, true, ParseExceptionType.ForbiddenArgument);
        testIntegerOptionWithoutValue(false, ParameterType.Forbidden, false,
                null);

        // required value
        testIntegerOptionWithValue(false, ParameterType.Required, correctInput,
                false, null);
        testIntegerOptionWithValue(false, ParameterType.Required,
                incorrectInput, true, ParseExceptionType.ArgumentOutOfBounds);
        testIntegerOptionWithoutValue(false, ParameterType.Required, true,
                ParseExceptionType.RequiredOptionNotUsed);
    }
    
    
    @Test
    public void testRequiredIntegerOption() throws ParseException {
        IntegerTestingData[] correctInput = createCorrectIntegerData();
        IntegerTestingData[] incorrectInput = createIncorrectIntegerData();

        // opntional value
        testIntegerOptionWithValue(true, ParameterType.Optional, correctInput,
                false, null);
        testIntegerOptionWithValue(true, ParameterType.Optional,
                incorrectInput, true, ParseExceptionType.ArgumentOutOfBounds);
        testIntegerOptionWithoutValue(true, ParameterType.Optional, false,
                null);

        // forbidden value
        testIntegerOptionWithValue(true, ParameterType.Forbidden,
                correctInput, true, ParseExceptionType.ForbiddenArgument);

        // 2 possible errors (wrong input + forbidden value): expected
        // ForbiddenArgument exception
        testIntegerOptionWithValue(true, ParameterType.Forbidden,
                incorrectInput, true, ParseExceptionType.ForbiddenArgument);
        testIntegerOptionWithoutValue(true, ParameterType.Forbidden, false,
                null);

        // required value
        testIntegerOptionWithValue(true, ParameterType.Required, correctInput,
                false, null);
        testIntegerOptionWithValue(true, ParameterType.Required,
                incorrectInput, true, ParseExceptionType.ArgumentOutOfBounds);
        testIntegerOptionWithoutValue(true, ParameterType.Required, true,
                ParseExceptionType.RequiredOptionNotUsed);
    }

    private static Integer NEGATIVE_INTEGER = -1;

    @Test
    public void testIntegerWithSeperatedNegativeValue() throws ParseException {
        String[] arguments =
                { INTEGER_LONG_NAME_WITH_PREFIX, NEGATIVE_INTEGER.toString() };
        OptionParser parser =
                createParserWithIntegerOption(false, ParameterType.Optional,
                        INTEGER_SHORT_NAME, INTEGER_LONG_NAME,
                        new IntegerTestingData(-10, 0, NEGATIVE_INTEGER),
                        arguments, OTHER_OPTIONS_WITHOUT_PREFIX);

        try {
            parser.parse();
        } catch (ParseException exception) {

            System.out.println(" - undefined exception ");
            throw exception;
        }

        String errorMessageForShortOption =
                prepareErrorMessageForOptionWithValue(INTEGER_SHORT_NAME,
                        arguments);
        assertEquals(errorMessageForShortOption, NEGATIVE_INTEGER.toString(),
                parser.getResult(INTEGER_SHORT_NAME));

        String errorMessageFoLongOption =
                prepareErrorMessageForOptionWithValue(INTEGER_LONG_NAME,
                        arguments);
        assertEquals(errorMessageFoLongOption, NEGATIVE_INTEGER.toString(),
                parser.getResult(INTEGER_LONG_NAME));

    }

    @Test
    public void testIntegerOptionWithJoinedNegativeValues()
            throws ParseException {

        IntegerTestingData[] correctInput = createCorrectNegativeIntegerData();
        testIntegerOptionWithValue(false, ParameterType.Optional, correctInput,
                false, null);

    }

    // -------------------------------------------
    // test required integer option
    // -------------------------------------------

    @Test
    public void testRequiredIntegerOptionWithUndefinedInput()
            throws ParseException {

        testRequiredIntegerWithEmptyInput(NO_ARGUMETNS);
        testRequiredIntegerWithEmptyInput(OTHER_OPTIONS_WITHOUT_PREFIX);
    }

    private void testRequiredIntegerWithEmptyInput(String[] inputArguments) {

        OptionParser parser =
                createParserWithIntegerOption(true, ParameterType.Optional,
                        INTEGER_SHORT_NAME, INTEGER_LONG_NAME,
                        CORRECT_ONE_INTEGER_INPUT, inputArguments,
                        OTHER_OPTIONS_WITHOUT_PREFIX);

        try {
            // TODO
            System.out.print(Arrays.toString(inputArguments));
            parser.parse();

            fail("Required integer option with " + ParameterType.Optional
                    + " value does not throw exception for input"
                    + Arrays.toString(inputArguments));

        } catch (ParseException exception) {

            assertEquals("Wrong type of exception at parsing forbiden value",
                    ParseExceptionType.RequiredOptionNotUsed, exception.type);
            // TODO
            System.out.print(" exception catched - OK");

        }

        System.out.println("");
    }

    // -------------------------------------------
    // -------------------------------------------
    // test string option
    // -------------------------------------------
    // -------------------------------------------

    @Test
    public void testStringOption() {
        OptionParser parser = new OptionParser(OTHER_OPTIONS_WITHOUT_PREFIX);
        parser.addOptionalIntegerOption("i", "integer", ParameterType.Optional,
                "", 0, 10, "");

        try {
            // TODO
            // System.out.print(Arrays.toString(inputArguments));
            parser.parse();
            /*
             * fail("Required boolean option with " + ParameterType.Optional +
             * " value does not throw exception for input" +
             * Arrays.toString(inputArguments));
             */

        } catch (ParseException exception) {

            assertEquals("Wrong type of exception at parsing forbiden va lue",
                    ParseExceptionType.RequiredOptionNotUsed, exception.type);
            // TODO
            System.out.print(" exception catched - OK");

        }

        System.out.println("");

    }

    /**
     * test: unparsed - options - arguments - exceptions
     * 
     * parse: - empty arguments - other type arguments - optional + value of
     * other type -
     * 
     * parsed all required/all optional x entered/missing arguments
     * 
     * with/without --
     * 
     * value by =
     * 
     * 
     * 
     */

}
